﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.Text;
using CoreEx.Wcf.Common;
using LinFu.AOP.Interfaces;
using LinFu.IoC.Configuration;
using LinFu.IoC.Interfaces;
using LinFu.IoC;
using LinFu.Proxy.Interfaces;
using CoreEx.Common.Proxy;
namespace CoreEx.Wcf.Server.Implementation
{
    /// <summary>
    /// A custom service host that allows services to be provided by an <see cref="IServiceContainer"/> instance.
    /// </summary>
    [Implements(typeof(ServiceHost))]
    public class ServiceContainerServiceHost : ServiceHost, IInitialize
    {        
        /// <summary>
        /// The type of the hosted service.
        /// </summary>
        private readonly Type _serviceType;
        
        /// <summary>
        /// The base adresses for the hosted service
        /// </summary>
        private readonly Uri[] _baseAddresses;

        /// <summary>
        /// Initializes a new instance of the <see cref="ServiceContainerServiceHost"/> class.
        /// </summary>
        /// <param name="serviceType">The type of service to host.</param>
        /// <param name="baseAddresses">The base adresses of the hosted service.</param>
        public ServiceContainerServiceHost(Type serviceType, params Uri[] baseAddresses) 
        {
            _serviceType = serviceType;
            _baseAddresses = baseAddresses;
        }
     
        public void Initialize(IServiceContainer source)
        {     
            //Check to see if the service instance implements IActualTarget 
            //and return the actual target if it does            
            Func<object> getTarget = () =>
                                         {
                                             var service = source.GetService(_serviceType);
                                             if (service is IActualTarget)
                                                 return ((IActualTarget) service).Instance;
                                             return service;
                                         };
            
            

            Func<IInterceptor> getInterceptor = 
            () => source.GetService<IInterceptor>("TargetInjector", getTarget);

            IConstructorInterceptor interceptorInjector = source.GetService<IConstructorInterceptor>("InterceptorInjector", getInterceptor);


            var serviceContractType =
                source.GetService<IServiceContractBuilder>("ServiceContractBuilder").CreateServiceContract(_serviceType,false);



            var serviceProxyType = source.GetService<IProxyFactory>().CreateProxyType(serviceContractType, new Type[] { },interceptorInjector);

            

            
            InitializeDescription(serviceProxyType, new UriSchemeKeyedCollection(_baseAddresses));

            

            
            var serviceEndpoint = AddServiceEndpoint(serviceProxyType, source.GetService<Binding>(),
                                                     "");
                        
            //Replace the default DataContractSerializer so that we can support object references and 
            //inject the known types into the serializer
            foreach (var operationDescription in serviceEndpoint.Contract.Operations)
            {                   
                var defaultSerializerBehavior = operationDescription.Behaviors.Find<DataContractSerializerOperationBehavior>();
                var customSerializerBehavior =
                    source.GetService<IOperationBehavior>("CustomSerializerOperationBehavior", operationDescription);
                if (defaultSerializerBehavior != null)
                    operationDescription.Behaviors.Remove(defaultSerializerBehavior);
                operationDescription.Behaviors.Add(customSerializerBehavior);
            }

            var serviceBehavours = source.GetServices<IServiceBehavior>();

            foreach (var serviceBehavior in serviceBehavours)
            {
                Description.Behaviors.Add(serviceBehavior);
            }



            ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
            //smb.HttpGetEnabled = true;

            Description.Behaviors.Add(smb);

            var debugBehavior = Description.Behaviors.Find<ServiceDebugBehavior>();                        
            if (debugBehavior != null)
            {                
                debugBehavior.IncludeExceptionDetailInFaults = true;
            }
            else
            {
                debugBehavior = new ServiceDebugBehavior();
                debugBehavior.IncludeExceptionDetailInFaults = true;
                Description.Behaviors.Add(debugBehavior);
            }


            

        }
    }
}